Context
包 context 定义了 Context 接口,Context 的具体实现包括 4 个方法,分别是 Deadline、Done、Err 和 Value
1 | type Context interface { |
Context 中实现了 2 个常用的生成顶层 Context 的方法。context.Background() 和 context.TODO()。
WithValue
WithValue 基于 parent Context 生成一个新的 Context,保存了一个 key-value 键值对。它常常用来传递上下文。WithValue 方法其实是创建了一个类型为 valueCtx 的 Context,它的类型定义如下:
1 | type valueCtx struct { |
它覆盖了 Value 方法,优先从自己的存储中检查这个 key,不存在的话会从 parent 中继续检查。
1 | ctx = context.TODO() |
WithCancel
WithCancel 方法返回 parent 的副本,只是副本中的 Done Channel 是新建的对象,它的类型是 cancelCtx。
1 | func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { |
代码中调用的 propagateCancel 方法会顺着 parent 路径往上找,直到找到一个 cancelCtx,或者为 nil。如果不为空,就把自己加入到这个 cancelCtx 的 child,以便这个 cancelCtx 被取消的时候通知自己。如果为空,会新起一个 goroutine,由它来监听 parent 的 Done 是否已关闭。
cancel 是向下传递的,如果一个 WithCancel 生成的 Context 被 cancel 时,如果它的子 Context(也有可能是孙,或者更低,依赖子的类型)也是 cancelCtx 类型的,就会被 cancel,但是不会向上传递。parent Context 不会因为子 Context 被 cancel 而 cancel。
WithTimeout
WithTimeout 其实是和 WithDeadline 一样,只不过一个参数是超时时间,一个参数是截止时间
1 | func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { |
WithDeadline
WithDeadline 会返回一个 parent 的副本,并且设置了一个不晚于参数 d 的截止时间,类型为 timerCtx(或者是 cancelCtx)。
如果它的截止时间晚于 parent 的截止时间,那么就以 parent 的截止时间为准,并返回一个类型为 cancelCtx 的 Context,因为 parent 的截止时间到了,就会取消这个 cancelCtx。如果当前时间已经超过了截止时间,就直接返回一个已经被 cancel 的 timerCtx。
否则就会启动一个定时器,到截止时间取消这个 timerCtx。
综合起来,timerCtx 的 Done 被 Close 掉,主要是由下面的某个事件触发的:
- 截止时间到了;
- cancel 函数被调用;
- parent 的 Done 被 close。
1 |
|